package cn.zf233.xcloud.filter;

import cn.zf233.xcloud.commom.Const;
import cn.zf233.xcloud.commom.R;
import cn.zf233.xcloud.commom.enums.UserStatusENUM;
import cn.zf233.xcloud.service.UserService;
import cn.zf233.xcloud.util.CommonUtil;
import cn.zf233.xcloud.util.RedisUtil;
import cn.zf233.xcloud.vo.UserVo;
import org.apache.commons.lang.StringUtils;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * Created by zf233 on 2021/8/20
 */
public class SessionStatusFilter implements Filter {

    private final RedisUtil redisUtil;
    private final UserService userService;

    public SessionStatusFilter(RedisUtil redisUtil, UserService userService) {
        this.redisUtil = redisUtil;
        this.userService = userService;
    }

    @Override
    public void doFilter(ServletRequest servletRequest,
                         ServletResponse servletResponse,
                         FilterChain filterChain) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        // 获取当前用户及remember me cookie
        UserVo current = (UserVo) request.getSession().getAttribute(Const.CURRENT_USER);
        Long id = getRememberId(request);

        /*
           如果当前用户为空，且其remember me cookie标志存在，即登录时勾选了记住我。
           如果用户信息属实，那么填充此用户信息到session，完成记住我功能。
         */
        if (current == null && id != null) {
            R<UserVo> rGetUserUid = userService.getUserVoByPrimaryKey(id);

            // 通过记住我进行快速登录成功
            if (rGetUserUid.isSuccess()) {

                if (UserStatusENUM.LOCK_USER.getCode().equals(rGetUserUid.getData().getStatus())) {

                    // 状态不正常则设置cookie超时、移除redis登陆标志
                    redisUtil.remove(Const.USER_SESSION_STATUS_PREFIX + id);
                    response.addCookie(forgetMe());
                } else { // 状态正常

                    // 记录用户当天登陆过的标志
                    userService.userLoginSuccessRecord(rGetUserUid.getData().getId());

                    // 填充用户信息
                    request.getSession().setAttribute(Const.CURRENT_USER, rGetUserUid.getData());
                    current = rGetUserUid.getData();
                }
            } else {

                // 失败则设置cookie超时、移除redis登陆标志
                redisUtil.remove(Const.USER_SESSION_STATUS_PREFIX + id);
                response.addCookie(forgetMe());
            }
        }

        // 如果cookie存在，则判断其对应的Redis登陆标志是否存在、超时状态
        if (current != null && id != null) {
            String key = Const.USER_SESSION_STATUS_PREFIX + id;

            // cooke存在但Redis标志不存在，移除客户端remember me cookie，取消其记住我状态
            if (!redisUtil.exists(key)) {
                response.addCookie(forgetMe());
            } else if (redisUtil.exists(key)
                    && redisUtil.ttl(key) < TimeUnit.MINUTES.toSeconds(30L)) {

                /*
                   cookie存在且Redis标志存在，但将要过期。在此重新设置客户端cookie、Redis标志的超时时间为7天。
                   （这里设计为remember me cookie、redis登陆标志两者超时时间一致，保持其对应关系。
                   保证redis登陆标志所提供的用户访问状态、访问IP等信息记录的实时性）
                 */
                Cookie cookie = new Cookie(Const.USER_REMEMBER_ME_COOKIE_KEY,
                        CommonUtil.base64Encoder(String.valueOf(id)));
                redisUtil.set(key, CommonUtil.getRealIp(request), Const.USER_REMEMBER_ME_TIMEOUT);
                cookie.setMaxAge((int) Const.USER_REMEMBER_ME_TIMEOUT);
                cookie.setPath("/");
                response.addCookie(cookie);
            }
        } else if (current != null) {

            /*
              如果当前用户为空且cookie不存在，那么认为此时用户并未勾选记住我或客户端记住我cookie已失效，
              直接将redis登陆标志超时时间设置为session默认超时时间即可
             */
            redisUtil.set(Const.USER_SESSION_STATUS_PREFIX + current.getId(),
                    CommonUtil.getRealIp(request), TimeUnit.MINUTES.toSeconds(30L));
        }

        // 放行
        filterChain.doFilter(request, response);
    }

    // Base64 解码 remember me cookie 中所存放的用户id
    private Long getRememberId(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null || cookies.length == 0) {
            return null;
        }
        for (Cookie cookie : cookies) {
            if (!Const.USER_REMEMBER_ME_COOKIE_KEY.equals(cookie.getName())) {
                continue;
            }
            String idStr = CommonUtil.base64Decoder(cookie.getValue());
            if (StringUtils.isBlank(idStr)) {
                break;
            }
            return new Long(idStr);
        }
        return null;
    }

    private Cookie forgetMe() {
        Cookie cookie = new Cookie(Const.USER_REMEMBER_ME_COOKIE_KEY, "");
        cookie.setMaxAge(0);
        cookie.setPath("/");
        return cookie;
    }
}
